﻿/*  ----------------------------------------------------------------------------
 *  EcoSqlCondition (http://ecosqlcondition.codeplex.com)
 *  ----------------------------------------------------------------------------
 *  License:    http://ecosqlcondition.codeplex.com/license  
 *  ----------------------------------------------------------------------------
 *  Company:    P3-Studio (http://www.p3studio.pl)
 *  File:       SqlPsService.cs
 *  Author:     Paweł Pawłow
 *  ----------------------------------------------------------------------------
 */

using System;
using System.Collections.Generic;
using System.Linq;
using Eco.Cache;
using Eco.DataRepresentation;
using Eco.Framework;
using Eco.FrameworkImpl.Ocl;
using Eco.Logging;
using Eco.ObjectRepresentation;
using Eco.Ocl.Impl;
using Eco.Ocl.Support;
using Eco.Services;
using Eco.Support;
using Eco.UmlRt;

namespace Eco.Persistence
{
	public interface ISqlPsService
	{
		/// <summary>
		/// Executes SQL statement to retrieve a list of objects.
		/// </summary>
		/// <param name="classType">The type of the objects that will be returned.</param>
		/// <param name="sql">Start with select eco_id, eco_type from ...</param>
		/// <returns>A collection of objects retrieved by SQL.</returns>
		IObjectList Execute(Type classType, string sql);

		/// <summary>
		/// Executes SQL statement to retrieve a list of objects.
		/// </summary>
		/// <param name="classType">The type of the objects that will be returned.</param>
		/// <param name="sql">Start with select eco_id, eco_type from ...</param>
		/// <param name="maxAnswers">The maximum number of rows to be processes by the SQL.</param>
		/// <param name="offset">The offset of the first row processed.</param>
		/// <returns>A collection of objects retrieved by SQL.</returns>
		IObjectList Execute(Type classType, string sql, int maxAnswers, int offset);

		/// <summary>
		/// Executes SQL statement to retrieve a list of objects.
		/// </summary>
		/// <param name="classType">The type of the objects that will be returned.</param>
		/// <param name="sql">Start with select eco_id, eco_type from ...</param>
		/// <param name="variableList">Collection of variables for the SQL statement.</param>
		/// <returns>A collection of objects retrieved by SQL.</returns>
		IObjectList Execute(Type classType, string sql, IExternalVariableList variableList);

		/// <summary>
		/// Executes SQL statement to retrieve a list of objects.
		/// </summary>
		/// <param name="classType">The type of the objects that will be returned.</param>
		/// <param name="sql">Start with select eco_id, eco_type from ...</param>
		/// <param name="variableList">Collection of variables for the SQL statement.</param>
		/// <param name="maxAnswers">The maximum number of rows to be processes by the SQL.</param>
		/// <param name="offset">The offset of the first row processed.</param>
		/// <returns>A collection of objects retrieved by SQL.</returns>
		IObjectList Execute(Type classType, string sql, IExternalVariableList variableList, int maxAnswers, int offset);

	}

	public class SqlPsServiceImpl : ISqlPsService
	{
		private readonly IObjectRepresentationProvider _objRepProv;
		private readonly ICache _cache;
		private readonly IEcoServiceProvider _serviceProvider;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="objRepProv"></param>
		/// <param name="serviceProvider"></param>
		/// <param name="cache"></param>
		public SqlPsServiceImpl(IEcoServiceProvider serviceProvider, ICache cache, IObjectRepresentationProvider objRepProv)
		{
			_objRepProv = objRepProv;
			_cache = cache;
			_serviceProvider = serviceProvider;
		}

		protected ObjectId ElementToObjectId(IElement obj)
		{
			ObjectId result = null;
			if (obj is IObject)
			{
				var l = _objRepProv.LocatorForIObject(((IObject)obj).ObjectInstance);
				if (l == null)
				{
					return result;
				}
				var temp = _cache.GetIdByLocator(l);
				if (temp.IsStorable)
				{
					result = temp;
				}
			}
			return result;
		}

		private IEnumerable<TBoldOLWVariableBinding> ExternalVariableListToBoldOlwVariableBinding(IExternalVariableList variableList)
		{
			if (variableList != null)
			{
				for (int i = 0; i < variableList.Count; i++)
				{
					var v = variableList[i];
					var classifier = v.StaticUmlType;
					if (classifier == null)
						throw new EBoldOCLError(string.Format("Variable {0} lacks type", v.Name));
					IOclType oclType = new OclTypeImpl(classifier);

					int topSortedIndex = -1;
					if (oclType.IsObjectType())
					{
						topSortedIndex = ((IClass)oclType.UMLType).InternalIndex;
					}
					else if (oclType.IsObjectListType())
					{
						topSortedIndex = ((IClass)oclType.ElementType.UMLType).InternalIndex;
					}
					var varBind = new TBoldOLWVariableBinding(i, v.Name, topSortedIndex);
					if (oclType.HasUmlType())
					{
						varBind.ClassifierName = oclType.UMLType.Name;
					}
					if (oclType.IsObjectType())
					{
						varBind.ExternalVarvalue = ElementToObjectId(v.Element);
					}
					else if (oclType.IsObjectListType())
					{
						var idList = new ObjectIdList();
						idList.AddRange(from obj in (IObjectList)v.Element select ElementToObjectId(obj));
						varBind.ExternalVarvalue = idList;
					}
					else if (v.Element.ContentType == ContentType.Primitive)
					{
						varBind.ExternalVarvalue = v.Element.AsObject;
					}
					yield return varBind;
				}
			}
		}

		IClass TypeToClass(Type classType)
		{
			return _serviceProvider.GetEcoService<ITypeSystemService>().TypeSystem.GetClassByType(classType);
		}

		private SqlCondition CreateSqlCondition(IClass selectedClass, string sql, IExternalVariableList variableList)
		{
			EcoSupport.TraceLogConditional(EcoLogSwitches.LogSql, sql.Trim(), "Eco.Sql");
			var sqlCondition = new SqlCondition(selectedClass.InternalIndex, sql);
			if (variableList != null)
				sqlCondition.Env.AddRange(ExternalVariableListToBoldOlwVariableBinding(variableList));
			return sqlCondition;
		}

		#region Implementation of ISqlPsService

		/// <summary>
		/// Executes SQL statement to retrieve a list of objects.
		/// </summary>
		/// <param name="classType">The type of the objects that will be returned.</param>
		/// <param name="sql">Start with select eco_id, eco_type from ...</param>
		/// <returns>A collection of objects retrieved by SQL.</returns>
		public IObjectList Execute(Type classType, string sql)
		{
			return Execute(classType, sql, null, -1, 0);
		}

		/// <summary>
		/// Executes SQL statement to retrieve a list of objects.
		/// </summary>
		/// <param name="classType">The type of the objects that will be returned.</param>
		/// <param name="sql">Start with select eco_id, eco_type from ...</param>
		/// <param name="maxAnswers">The maximum number of rows to be processes by the SQL.</param>
		/// <param name="offset">The offset of the first row processed.</param>
		/// <returns>A collection of objects retrieved by SQL.</returns>
		public IObjectList Execute(Type classType, string sql, int maxAnswers, int offset)
		{
			return Execute(classType, sql, null, maxAnswers, offset);
		}

		/// <summary>
		/// Executes SQL statement to retrieve a list of objects.
		/// </summary>
		/// <param name="classType">The type of the objects that will be returned.</param>
		/// <param name="sql">Start with select eco_id, eco_type from ...</param>
		/// <param name="variableList">Collection of variables for the SQL statement.</param>
		/// <returns>A collection of objects retrieved by SQL.</returns>
		public IObjectList Execute(Type classType, string sql, IExternalVariableList variableList)
		{
			return Execute(classType, sql, variableList, -1, 0);
		}

		/// <summary>
		/// Executes SQL statement to retrieve a list of objects.
		/// </summary>
		/// <param name="classType">The type of the objects that will be returned.</param>
		/// <param name="sql">Start with select eco_id, eco_type from ...</param>
		/// <param name="variableList">Collection of variables for the SQL statement.</param>
		/// <param name="maxAnswers">The maximum number of rows to be processes by the SQL.</param>
		/// <param name="offset">The offset of the first row processed.</param>
		/// <returns>A collection of objects retrieved by SQL.</returns>
		public IObjectList Execute(Type classType, string sql, IExternalVariableList variableList, int maxAnswers, int offset)
		{
			var cond = CreateSqlCondition(TypeToClass(classType), sql, variableList);
			var ps = _serviceProvider.GetEcoService<IPersistenceService>();
			return ps.GetAllWithCondition(cond, maxAnswers, offset);
		}

		#endregion
	}

}
